﻿' ----------------------------------------------------------------------
'
' Directory.vb
'
'   Dbfs.Domain
'
'   This class contains the implementation of a single folder.
'
' ----------------------------------------------------------------------
' version:  1.0.0.1
' today:    06/05/10  07:55:00
' ident:    "@(#)/Dbfs/Domain/Directory.vb 1.0 0.2 06/05/10 0 rjs"

Imports System.IO
Imports System.Security.AccessControl
Imports System.Text.RegularExpressions
Imports System.Security
Imports Dbfs


Namespace Dbfs

    Public Class Directory

        ''' <summary>
        ''' Gets the current working directory of the application.
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks>The current directory is distinct from the original directory, 
        ''' which is the one from which the process was started.</remarks>
        Public Shared Function GetCurrentDirectory() As String

            'Dim uc As Hashtable = Dbfs.UserCache()
            Dim uc As Hashtable = New Hashtable
            Dim curdir As String

            ' get the user-specific cache of FileSystemItemSets
            If uc.ContainsKey("curdir") Then
                curdir = uc("curdir")
            Else
                curdir = DIRECTORY_SEP_CHAR
                uc.Add("curdir", curdir)
            End If

            Return curdir

        End Function

        ''' <summary>
        ''' Returns the directory information for the specified path string.
        ''' </summary>
        ''' <param name="path">The path of a file or directory.</param>
        ''' <returns></returns>
        ''' <remarks>The path returned does not include any UNC prefix.</remarks>
        Public Shared Function GetDirectoryName(ByVal path As String) As String

            If path.StartsWith(UNC_CHARS) Then
                Dim i As Int16 = path.IndexOf(DIRECTORY_SEP_CHAR, 2)
                path = path.Substring(i)
            End If

            Return System.IO.Path.GetDirectoryName(path)

        End Function

        ''' <summary>
        ''' Gets the filesystem name portion of a given path.
        ''' </summary>
        ''' <param name="path">The path of a file or directory.</param>
        ''' <returns>A string containing the filesystem name for the specified path.</returns>
        ''' <remarks></remarks>
        Public Shared Function GetFileSystemName(ByVal path As String) As String

            ' local variables
            Dim fileSystemName As String

            ' the dbfs instance to connect to is given by the high level
            ' qualifier in the path using the UNC convention
            If path.StartsWith(UNC_CHARS) Then

                Dim i As Integer

                If path.Length = 2 Then
                    Throw New ArgumentException("Invalid path")
                End If

                i = path.IndexOf(DIRECTORY_SEP_CHAR, 2)
                If i = 2 Then
                    Throw New ArgumentException("Invalid path")
                End If

                If i = -1 Then
                    fileSystemName = path
                Else
                    fileSystemName = path.Substring(0, i)
                End If

                If fileSystemName.IndexOfAny(System.IO.Path.GetInvalidPathChars()) <> -1 Then
                    Throw New ArgumentException("Invalid path")
                End If

                Return fileSystemName

            End If

            ' path does not start with "\\"
            Return String.Empty

        End Function

        ''' <summary>
        ''' Sets the application's current working directory to the specified directory.
        ''' </summary>
        ''' <param name="path">The path to which the current working directory is set.</param>
        ''' <remarks></remarks>
        Public Shared Sub SetCurrentDirectory(ByVal path As String)

            ' local variables
            Dim uc As Hashtable

            ' check for null path
            If path Is Nothing Then
                Throw New ArgumentNullException("path")
            End If

            ' check for valid path characters
            path = path.Trim()
            If path.Length = 0 OrElse _
               path.IndexOfAny(System.IO.Path.GetInvalidPathChars()) > 0 Then
                Throw New ArgumentException("Invalid path.")
            End If

            ' make sure the directory exists
            path = ResolvePath(GetCurrentDirectory(), path)

            ' get the user-specific information cache
            'uc = Dbfs.UserCache()
            uc = New Hashtable

            ' save 'path' as the current directory
            If uc.ContainsKey("curdir") Then
                uc("curdir") = path
            Else
                uc.Add("curdir", path)
            End If

        End Sub

        ''' <summary>
        ''' Moves a directory and its contents to a new location.
        ''' </summary>
        ''' <param name="sourceDirName">The path of the directory to move.</param>
        ''' <param name="destDirName">The path to the new location for sourceDirName.</param>
        ''' <remarks></remarks>
        Public Shared Sub Move(ByVal sourceDirName As String, ByVal destDirName As String)

            ' local variables

            ' validate arguments
            If sourceDirName Is Nothing Or destDirName Is Nothing Then
                Throw New ArgumentNullException("sourceDirName")
            End If

            If sourceDirName.Trim.Length = 0 Or sourceDirName.IndexOfAny(System.IO.Path.GetInvalidPathChars()) <> -1 Then
                Throw New ArgumentException("sourceDirName")
            End If

            If destDirName.Trim.Length = 0 Or destDirName.IndexOfAny(System.IO.Path.GetInvalidPathChars()) <> -1 Then
                Throw New ArgumentException("destDirName")
            End If

            If sourceDirName = destDirName Then
                Throw New IOException("The sourceDirName and destDirName parameters refer to the same directory.")
            End If

            ' get old/new paths/name
            Dim oldPath As String = ResolvePath(GetCurrentDirectory(), Path.GetDirectoryName(sourceDirName))
            Dim newPath As String = ResolvePath(GetCurrentDirectory(), Path.GetDirectoryName(destDirName))
            Dim oldName As String = Path.GetFileName(sourceDirName)
            Dim newName As String = Path.GetFileName(destDirName)

            Dim oldDatabaseName As String = GetFileSystemName(oldPath)
            Dim newDatabaseName As String = GetFileSystemName(newPath)
            Dim handle As Integer = 0

            ' the dbfs instance to connect to is given by the high level
            ' qualifier in the path using the UNC convention
            If oldDatabaseName.Length <> 0 Then
                ' get a shareable instance of the database
                'handle = Dbfs.Register(oldDatabaseName)
            End If

            If newDatabaseName.Length <> 0 Then
                Throw New IOException("An attempt was made to move a directory to a different volume.")
            End If

            ' need to remove references to the database name from the path
            Dim i As Integer
            If oldPath.StartsWith(UNC_CHARS) Then
                i = oldPath.IndexOf(DIRECTORY_SEP_CHAR, 2)
                oldPath = oldPath.Substring(i)
            End If

            ' if there's no destination directory then use source directory
            If newPath.Length = 0 Then
                newPath = oldPath
            Else
                If newPath.StartsWith(UNC_CHARS) Then
                    i = newPath.IndexOf(DIRECTORY_SEP_CHAR, 2)
                    newPath = newPath.Substring(i)
                End If
            End If

            ' if moving to a new directory, we need the new parent node id and
            ' we need to see if the new directory name is already in use


            ' if changes are being tracked we need an update an insert,
            ' otherwise we can just update in place

            ' changes are being tracked, so perform an update and insert



        End Sub

#Region "Friend Methods"
        ''' <summary>
        ''' Resolves relative path specifications in the given path and returns a
        ''' fully qualified path.
        ''' </summary>
        ''' <param name="currentDirectory">The origin path from which the relative path is derived.</param>
        ''' <param name="dirName">An absolute or relative path.</param>
        ''' <returns>A String containing directory information for path.</returns>
        ''' <remarks></remarks>
        Public Shared Function ResolvePath(ByVal currentDirectory As String, ByVal dirName As String) As String

            ' local variables
            Dim delimiters As Char() = New Char() {DIRECTORY_SEP_CHAR, ALT_DIRECTORY_SEP_CHAR}
            Dim searchString As String = "." & DIRECTORY_SEP_CHAR
            Dim altSearchString As String = "." & ALT_DIRECTORY_SEP_CHAR

            Dim addSep As Boolean = True
            Dim fsName As String = String.Empty
            Dim dirs As String()
            Dim i As Integer
            Dim j As Integer
            Dim resolvedPath As String

            ' validate arguments
            If dirName Is Nothing Then _
                Throw New ArgumentNullException(dirName)

            If currentDirectory Is Nothing Then _
                Throw New ArgumentNullException(currentDirectory)

            ' check if any of this is necessary
            If dirName = "" Then Return currentDirectory
            If dirName = "." Then Return currentDirectory
            If dirName = ".." Then
                Dim newPath As String = Path.GetDirectoryName(currentDirectory)
                If newPath Is Nothing Then
                    Return currentDirectory
                Else
                    Return newPath
                End If
            End If

            ' make sure the path is fully qualified
            ' (if the 'to' directory is fully qualified, the current directory
            '  is irrelevant)
            If Not dirName.StartsWith(DIRECTORY_SEP_CHAR) Then
                dirName = currentDirectory & DIRECTORY_SEP_CHAR & dirName
                addSep = False
            End If

            ' if there is no relative pathing, we're done
            If dirName.IndexOf(searchString) = -1 AndAlso _
               dirName.IndexOf(altSearchString) = -1 Then
                Return dirName
            End If

            ' remove any filesystem name prefix from the path
            If dirName.StartsWith(UNC_CHARS) Then
                i = dirName.IndexOfAny(DIRECTORY_SEP_CHARS, 2)
                If i = -1 Then
                    fsName = dirName
                    dirName = DIRECTORY_SEP_CHAR
                Else
                    fsName = dirName.Substring(0, i)
                    dirName = dirName.Substring(i + 1)
                End If
            End If

            ' split the path into it's component directories
            dirs = dirName.Split(delimiters)

            ' find the first path component
            For i = 0 To dirs.Length - 1
                If dirs(i) <> "" Then Exit For
            Next

            dirs(0) = dirs(i)
            j = 1

            For i = i + 1 To dirs.Length - 1
                ' remove references to the current directory
                If dirs(i) = "." Or dirs(i) = "" Then
                    Continue For
                End If

                ' remove references to the parent directory
                If dirs(i) = ".." Then
                    If j > 0 Then j -= 1
                    Continue For
                End If

                dirs(j) = dirs(i)
                j += 1
            Next

            ' reassemble the path
            resolvedPath = fsName

            For i = 0 To j - 1
                resolvedPath += DIRECTORY_SEP_CHAR & dirs(i)
            Next

            If addSep Then resolvedPath = DIRECTORY_SEP_CHAR & resolvedPath

            Return resolvedPath

        End Function
#End Region

    End Class

End Namespace
